#include "scenewidget.h"
#include "dm/dmdef.h"
#include "globalconfig.h"
#include "waitdialog.h"

#include <QFile>
#include <QFileDialog>
#include <QDateTime>
#include <QDataStream>

#include <cstring>

constexpr auto SCENE_SUFFIX = "scn";
constexpr auto SCENES_SUFFIX = "scns";

SceneWidget::SceneWidget(QWidget *parent)
    : AGroupBox{tr(LABEL_SCENE), parent}
{
    setFixedHeight(300);
    setDisabled(true);

    nameLabel = new ALabel(tr(LABEL_SCENE_NAME), this);
    lineEdit = new ALineEdit(this);
    currentLabel = new ALabel(tr(LABEL_CURRENT_SCENE), this);
    comboBox = new AComboBox(this);
    loadButton = new APushButton(tr(LABEL_LOAD), this);
    saveButton = new APushButton(tr(LABEL_SAVE), this);
    importButton = new APushButton(tr(LABEL_IMPORT), this);
    exportButton = new APushButton(tr(LABEL_EXPORT), this);
    importAllButton = new APushButton(tr(LABEL_IMPORT_ALL), this);
    exportAllButton = new APushButton(tr(LABEL_EXPORT_ALL), this);
    waitDialog = new WaitDialog(this);

    auto layout = new QGridLayout(this);
    layout->addWidget(nameLabel, 0, 0);
    layout->addWidget(lineEdit, 0, 1, 1, 3);
    layout->addWidget(currentLabel, 1, 0);
    layout->addWidget(comboBox, 1, 1, 1, 3);
    layout->addWidget(loadButton, 2, 0, 1, 2);
    layout->addWidget(saveButton, 2, 2, 1, 2, Qt::AlignRight);
    layout->addWidget(new AHLine(this), 3, 0, 1, 4);
    layout->addWidget(importButton, 4, 0, 1, 2);
    layout->addWidget(exportButton, 4, 2, 1, 2, Qt::AlignRight);
    layout->addWidget(new AHLine(this), 5, 0, 1, 4);
    layout->addWidget(importAllButton, 6, 0, 1, 2);
    layout->addWidget(exportAllButton, 6, 2, 1, 2, Qt::AlignRight);

    lineEdit->setMaxLength(NAME_LEN_GPR);
    lineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression("[ -~]+"), parent));
    lineEdit->setAlignment(Qt::AlignLeft);

    connect(lineEdit, &ALineEdit::textConfirmed, this, [=](const QString &text) {
        emit writeSceneName(comboBox->currentIndex() + 1, text);
    });

    connect(comboBox, &AComboBox::currentTextChanged, lineEdit, &ALineEdit::setText);

    connect(loadButton, &APushButton::clicked, this, &SceneWidget::loadClicked);

    connect(saveButton, &APushButton::clicked, this, &SceneWidget::saveClicked);

    connect(importButton, &APushButton::clicked, this, &SceneWidget::importClicked);

    connect(exportButton, &APushButton::clicked, this, &SceneWidget::exportClicked);

    connect(importAllButton, &APushButton::clicked, this, &SceneWidget::importAllClicked);

    connect(exportAllButton, &APushButton::clicked, this, &SceneWidget::exportAllClicked);

    connect(&GlobalConfig::getInstance(), &GlobalConfig::initialized, this, &SceneWidget::updateComboBox);

    connect(&GlobalConfig::getInstance(), &GlobalConfig::sceneNameUpdated, this, &SceneWidget::updateComboBox);

    connect(&GlobalConfig::getInstance(), &GlobalConfig::disconnected, this, [=]() {
        comboBox->clear();
        setDisabled(true);
    });
    connect(this, &SceneWidget::writeSceneName, &GlobalConfig::getInstance(), &GlobalConfig::writeSceneName);
    connect(this, &SceneWidget::loadScene, &GlobalConfig::getInstance(), &GlobalConfig::loadScene);
    connect(this, &SceneWidget::saveScene, &GlobalConfig::getInstance(), &GlobalConfig::saveScene);
    connect(this, &SceneWidget::exportScene, &GlobalConfig::getInstance(), &GlobalConfig::exportScene);
    connect(this, &SceneWidget::importScene, &GlobalConfig::getInstance(), &GlobalConfig::importScene);

    connect(&GlobalConfig::getInstance(), &GlobalConfig::loaded, waitDialog, &WaitDialog::close);
    connect(&GlobalConfig::getInstance(), &GlobalConfig::saved, waitDialog, &WaitDialog::close);
    connect(&GlobalConfig::getInstance(), &GlobalConfig::imported, this, [=](uint8_t index) {
        m_import.remove(index);
        if (m_import.isEmpty()) {
            waitDialog->close();
        }
    });
    connect(&GlobalConfig::getInstance(), &GlobalConfig::exported, this, &SceneWidget::exported);
}

SceneWidget::~SceneWidget()
{

}

void SceneWidget::updateComboBox()
{
    const auto &group = GlobalConfig::getInstance().cfgDev().cfg.group;
    const auto &cfgName = GlobalConfig::getInstance().cfgName();
    if (comboBox->count() > 0) {
        for (int i = 1; i < group; ++i) {
            comboBox->setItemText(i - 1, cfgName.user[i]);
        }
    }
    else {
        for (int i = 1; i < group; ++i) {
            comboBox->addItem(cfgName.user[i]);
        }
    }
    setEnabled(group);
}

QString SceneWidget::filePath(const QString &suffix)
{
    const auto cfg = GlobalConfig::getInstance().cfgDev().cfg;
    return QFileDialog::getSaveFileName(this, tr("Export File"),
                                 QString("%1_V%2_%3").arg(cfg.name, QString::number(cfg.ver_boot[0]), QDateTime::currentDateTime().toString("yyyyMMdd")),
                                 tr("Scene (*.%1)").arg(suffix));
}

void SceneWidget::loadClicked()
{
    emit loadScene(comboBox->currentIndex() + 1);
    waitDialog->exec();
}

void SceneWidget::saveClicked()
{
    emit saveScene(comboBox->currentIndex() + 1);
    waitDialog->exec();
}

void SceneWidget::importClicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Import File"),
                                                    "",
                                                    tr("Scene (*.%1)").arg(SCENE_SUFFIX));
    if (fileName.isEmpty()) {
        return;
    }
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        return ;
    }
    QByteArray fileData = file.readAll();
    file.close();
    constexpr size_t size = sizeof(PARAM_GROUP);
    if (fileData.size() % size != 0) {
        QMessageBox::warning(this, tr("Warning"), tr("File error!"));
        return ;
    }
    std::vector<uint8_t> result(fileData.size());
    std::memcpy(result.data(), fileData.data(), fileData.size());
    emit importScene(0, result);
    waitDialog->exec();
}

void SceneWidget::exportClicked()
{
    emit exportScene(0);    // 当前场景
    waitDialog->exec();
}

void SceneWidget::importAllClicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Import File"),
                                                    "",
                                                    tr("Scene (*.%1)").arg(SCENES_SUFFIX));
    if (fileName.isEmpty()) {
        return;
    }

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        return ;
    }

    QByteArray fileData = file.readAll();
    file.close();

    constexpr size_t size = sizeof(PARAM_GROUP);
    if (fileData.isEmpty() || fileData.size() % size != 0) {
        QMessageBox::warning(this, tr("Warning"), tr("File error!"));
        return ;
    }

    for (size_t i = 0; i < fileData.size() / size; ++i) {
        std::vector<uint8_t> result(sizeof(PARAM_GROUP));
        std::memcpy(result.data(), fileData.data() + i * size, size);
        m_import.insert(i + 1, result);
    }
    if (!m_import.isEmpty()) {
        importAll();
        waitDialog->exec();
    }
}

void SceneWidget::exportAllClicked()
{
    if (comboBox->count() > 0) {
        exportAll();
        waitDialog->exec();
    }
}

void SceneWidget::importAll()
{
    if (!m_import.isEmpty()) {
        for (auto it = m_import.begin(); it != m_import.end(); ++it) {
            emit importScene(it.key(), it.value());
        }
        QTimer::singleShot(10000, this, &SceneWidget::importAll);   // 重发判定
    }
}

void SceneWidget::exportAll()
{
    if (m_export.count() != comboBox->count()) {    // 未导出完全
        for (uint8_t i = 1; i <= comboBox->count(); ++i) {
            if (!m_export.contains(i)) {    // 请求未导出场景数据
                emit exportScene(i);
            }
        }
        QTimer::singleShot(10000, this, &SceneWidget::exportAll);   // 重发判定
    }
}

void SceneWidget::exported(uint8_t cmd3, const std::vector<uint8_t> &data)
{
    if (0 == cmd3) {
        waitDialog->close();
        QString fileName = filePath(SCENE_SUFFIX);
        if (fileName.isEmpty()) {
            return;
        }
        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly)) {
            return ;
        }
        qint64 bytesWritten = file.write(reinterpret_cast<const char*>(data.data()), static_cast<qint64>(data.size()));

        if (bytesWritten != static_cast<qint64>(data.size())) {
            file.close();
            return ;
        }

        file.close();
    }
    else {
        m_export.insert(cmd3, data);
        if (m_export.size() != comboBox->count()) {
            return ;
        }
        waitDialog->close();
        QString fileName = filePath(SCENES_SUFFIX);
        if (fileName.isEmpty()) {
            return;
        }
        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly)) {
            return ;
        }
        for (auto i = 0; i < m_export.size(); ++i) {
            const auto &scene = m_export.value(i + 1);
            qint64 bytesWritten = file.write(reinterpret_cast<const char*>(scene.data()), static_cast<qint64>(scene.size()));
            if (bytesWritten != static_cast<qint64>(scene.size())) {
                file.close();
                return ;
            }
        }

        file.close();
        m_export.clear();
    }
}

